package com.cbs.android.component.runtimecache;

import android.content.Context;
import android.content.SharedPreferences;

import com.cbs.android.component.log.L;

import java.util.Hashtable;
import java.util.Observer;

/**
 * Created by brochexu on 4/14/14.
 */
public class RuntimeCache {
    private static final String TAG = RuntimeCache.class.getName();
    protected Hashtable<Class<? extends RuntimeCacheItem>, RuntimeCacheItem> cacheItems = new Hashtable<>();
    protected SharedPreferences sharedPreferences;

    public RuntimeCache(Context context, String name) {
        sharedPreferences = context.getSharedPreferences(name, Context.MODE_PRIVATE);
    }

    public synchronized Object getValue(Class<? extends RuntimeCacheItem> key) {
        try {
            if (cacheItems.containsKey(key)) {
                return cacheItems.get(key).getValue();
            } else {
                return _addCache(key).getValue();
            }
        } catch (Exception e) {
            L.e(TAG, "", e);
        }
        return null;
    }

    public synchronized RuntimeCacheItem getItem(Class<? extends RuntimeCacheItem> key) {
        try {
            if (cacheItems.containsKey(key)) {
                return cacheItems.get(key);
            } else {
                return _addCache(key);
            }
        } catch (Exception e) {
            L.e(TAG, "", e);
        }
        return null;
    }

    public synchronized boolean setValue(Class<? extends RuntimeCacheItem> key, Object value) {
        try {
            RuntimeCacheItem runtimeCacheItem;
            if (cacheItems.containsKey(key)) {
                runtimeCacheItem = cacheItems.get(key);
            } else {
                runtimeCacheItem = _addCache(key);
            }

            runtimeCacheItem.setValue(value);
            if (runtimeCacheItem.isSave()) {
                setEditor(runtimeCacheItem);
            }

            if (runtimeCacheItem.isNotify()) {
                runtimeCacheItem.notifyObs();
            }
            return true;
        } catch (Exception e) {
            L.e(TAG, "", e);
        }
        return false;
    }

    public synchronized boolean reset(Class<? extends RuntimeCacheItem> key) {
        try {
            RuntimeCacheItem runtimeCacheItem;
            if (cacheItems.containsKey(key)) {
                runtimeCacheItem = cacheItems.get(key);
            } else {
                runtimeCacheItem = _addCache(key);
            }

            runtimeCacheItem.reset();
            if (runtimeCacheItem.isSave()) {
                setEditor(runtimeCacheItem);
            }

            if (runtimeCacheItem.isNotify()) {
                runtimeCacheItem.notifyObs();
            }
            return true;
        } catch (Exception e) {
            L.e(TAG, "", e);
        }
        return false;
    }

    public synchronized boolean resetAll() {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.clear();
        editor.commit();
        try {
            for (Class key : cacheItems.keySet()) {
                RuntimeCacheItem runtimeCacheItem = cacheItems.get(key);
                runtimeCacheItem.reset();

                if (runtimeCacheItem.isSave()) {
                    setEditor(runtimeCacheItem);
                }
                if (runtimeCacheItem.isNotify()) {
                    runtimeCacheItem.notifyObs();
                }
            }
            editor.commit();
            return true;
        } catch (Exception e) {
            L.e(TAG, "", e);
        }
        return false;
    }

    public synchronized boolean remove(Class<? extends RuntimeCacheItem> key) {
        try {
            if (cacheItems.containsKey(key)) {
                cacheItems.remove(key);
            }
            if (sharedPreferences.contains(key.getName())) {
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.remove(key.getName());
                editor.commit();
            }
            return true;
        } catch (Exception e) {
            L.e(TAG, "", e);
        }
        return false;
    }

    public synchronized boolean clear() {
        try {
            cacheItems.clear();
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.clear();
            editor.commit();
            return true;
        } catch (Exception e) {
            L.e(TAG, "", e);
        }
        return false;
    }

    public synchronized boolean addObserver(Class<? extends RuntimeCacheItem> key, Observer observer) {
        try {
            if (cacheItems.containsKey(key)) {
                cacheItems.get(key).addObserver(observer);
            } else {
                _addCache(key).addObserver(observer);
            }
            return true;
        } catch (Exception e) {
            L.e(TAG, "", e);
        }
        return false;
    }

    public synchronized boolean deleteObserver(Class<? extends RuntimeCacheItem> key, Observer observer) {
        if (cacheItems.containsKey(key)) {
            cacheItems.get(key).deleteObserver(observer);
        }
        return true;
    }

    protected synchronized RuntimeCacheItem _addCache(Class<? extends RuntimeCacheItem> key) throws IllegalAccessException, InstantiationException {
        RuntimeCacheItem runtimeCacheItem = key.newInstance();
        boolean cacheinitialized = false;
        cacheItems.put(key, runtimeCacheItem);

        if (runtimeCacheItem.isSave()) {
            String skey = key.getName();
            int dataType = runtimeCacheItem.getDataType();
            if (sharedPreferences.contains(skey)) {
                if (dataType == RuntimeCacheItem.DataType_Boolean) {
                    runtimeCacheItem.setValue(sharedPreferences.getBoolean(skey, (Boolean) runtimeCacheItem.getDefaultValue()));
                    cacheinitialized = true;
                } else if (dataType == RuntimeCacheItem.DataType_Float) {
                    runtimeCacheItem.setValue(sharedPreferences.getFloat(skey, (Float) runtimeCacheItem.getDefaultValue()));
                    cacheinitialized = true;
                } else if (dataType == RuntimeCacheItem.DataType_Int) {
                    runtimeCacheItem.setValue(sharedPreferences.getInt(skey, (Integer) runtimeCacheItem.getDefaultValue()));
                    cacheinitialized = true;
                } else if (dataType == RuntimeCacheItem.DataType_Long) {
                    runtimeCacheItem.setValue(sharedPreferences.getLong(skey, (Long) runtimeCacheItem.getDefaultValue()));
                    cacheinitialized = true;
                } else if (dataType == RuntimeCacheItem.DataType_String) {
                    runtimeCacheItem.setValue(sharedPreferences.getString(skey, (String) runtimeCacheItem.getDefaultValue()));
                    cacheinitialized = true;
                } else if (dataType == RuntimeCacheItem.DataType_Object) {
                    runtimeCacheItem.setValue(runtimeCacheItem.decode(sharedPreferences.getString(skey, runtimeCacheItem.encode(runtimeCacheItem.getDefaultValue()))));
                    cacheinitialized = true;
                } else {
                    runtimeCacheItem.setValue(sharedPreferences.getString(skey, (String) runtimeCacheItem.getDefaultValue()));
                    cacheinitialized = true;
                }
            }
            if (!cacheinitialized) {
                Object dvalue = runtimeCacheItem.getDefaultValue();
                runtimeCacheItem.setValue(dvalue);
                setEditor(runtimeCacheItem);
            }
        } else {
            runtimeCacheItem.setValue(runtimeCacheItem.getDefaultValue());
        }
        return runtimeCacheItem;
    }

    protected void setEditor(RuntimeCacheItem item) {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        int dataType = item.getDataType();
        Object dvalue = item.getValue();
        String skey = item.getClass().getName();
        if (dataType == RuntimeCacheItem.DataType_Boolean) {
            editor.putBoolean(skey, (Boolean) dvalue);
        } else if (dataType == RuntimeCacheItem.DataType_Float) {
            editor.putFloat(skey, (Float) dvalue);
        } else if (dataType == RuntimeCacheItem.DataType_Int) {
            editor.putInt(skey, (Integer) dvalue);
        } else if (dataType == RuntimeCacheItem.DataType_Long) {
            editor.putLong(skey, (Long) dvalue);
        } else if (dataType == RuntimeCacheItem.DataType_String) {
            editor.putString(skey, (String) dvalue);
        } else if (dataType == RuntimeCacheItem.DataType_Object) {
            editor.putString(skey, item.encode(dvalue));
        } else {
            editor.putString(skey, (String) dvalue);
        }
        editor.commit();
    }
}
